package com.jacobson.core.widget.adapter

import android.view.ViewGroup
import androidx.collection.SparseArrayCompat
import androidx.recyclerview.widget.RecyclerView
import io.reactivex.internal.functions.ObjectHelper

/**
 * ================================================
 *
 *  @author Wucz on 2018/10/9 17:57
 * ================================================
 */

class AdapterDelegatesManager<T> {
    private var delegates = SparseArrayCompat<AdapterDelegate<T>>()
    var fallbackDelegate: AdapterDelegate<T>? = null

    fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        val delegate = getDelegateForViewType(viewType)
        ObjectHelper.requireNonNull<AdapterDelegate<T>>(delegate, "No AdapterDelegate added for ViewType $viewType")

        val viewHolder = delegate!!.onCreateViewHolder(parent)
        ObjectHelper.requireNonNull<RecyclerView.ViewHolder>(viewHolder,
                "ViewHolder returned from AdapterDelegate $delegate for ViewType =$viewType is null!")
        return viewHolder
    }

    fun onBindViewHolder(items: T?, viewHolder: RecyclerView.ViewHolder, position: Int, payloads: MutableList<Any>?) {
        val delegate = findUsableDelegate(viewHolder)
        if (payloads?.isNotEmpty() == true) {
            delegate.onBindViewHolder(items, viewHolder, position, payloads)
        } else {
            delegate.onBindViewHolder(items, viewHolder, position)
        }
    }

    fun getItemViewType(items: T?, position: Int): Int {
        val delegatesCount = delegates.size()
        for (i in 0 until delegatesCount) {
            val delegate = delegates.valueAt(i)
            if (delegate.isForViewType(items, position)) {
                return delegates.keyAt(i)
            }
        }
        if (fallbackDelegate != null) {
            return FALLBACK_DELEGATE_VIEW_TYPE
        }
        throw NullPointerException("No AdapterDelegate added that matches position=$position in data source")
    }

    fun onViewRecycled(viewHolder: RecyclerView.ViewHolder) {
        findUsableDelegate(viewHolder).onViewRecycled(viewHolder)
    }

    fun onFailedToRecycleView(viewHolder: RecyclerView.ViewHolder): Boolean {
        return findUsableDelegate(viewHolder).onFailedToRecycleView(viewHolder)
    }

    fun onViewAttachedToWindow(viewHolder: RecyclerView.ViewHolder) {
        findUsableDelegate(viewHolder).onViewAttachedToWindow(viewHolder)
    }

    fun onViewDetachedFromWindow(viewHolder: RecyclerView.ViewHolder) {
        findUsableDelegate(viewHolder).onViewDetachedFromWindow(viewHolder)
    }

    fun addDelegate(delegate: AdapterDelegate<T>): AdapterDelegatesManager<T> {
        var viewType = delegates.size() + VIEW_TYPE_BASELINE
        while (delegates.get(viewType) != null) {
            viewType++
            if (viewType == FALLBACK_DELEGATE_VIEW_TYPE) {
                throw IllegalArgumentException("Oops, we are very close to Integer.MAX_VALUE. It seems that there are no more free and unused view type integers left to add another AdapterDelegate.")
            }
        }
        return addDelegate(viewType, false, delegate)
    }

    fun addDelegate(viewType: Int, delegate: AdapterDelegate<T>): AdapterDelegatesManager<T> {
        return addDelegate(viewType, false, delegate)
    }

    fun addDelegate(viewType: Int, allowReplacingDelegate: Boolean, delegate: AdapterDelegate<T>): AdapterDelegatesManager<T> {
        if (viewType == FALLBACK_DELEGATE_VIEW_TYPE) {
            throw IllegalArgumentException("The view type = "
                    + FALLBACK_DELEGATE_VIEW_TYPE
                    + " is reserved for fallback adapter delegate (see setFallbackDelegate() ). Please use another view type.")
        }

        if (!allowReplacingDelegate && delegates.get(viewType) != null) {
            throw IllegalArgumentException(
                    "An AdapterDelegate is already registered for the viewType = "
                            + viewType
                            + ". Already registered AdapterDelegate is "
                            + delegates.get(viewType))
        }

        delegates.put(viewType, delegate)
        return this
    }

    fun removeDelegate(delegate: AdapterDelegate<T>): AdapterDelegatesManager<T> {
        val indexToRemove = delegates.indexOfValue(delegate)
        if (indexToRemove > 0) {
            delegates.removeAt(indexToRemove)
        }
        return this
    }

    fun removeDelegate(viewType: Int): AdapterDelegatesManager<T> {
        delegates.remove(viewType)
        return this
    }

    fun clearDelegates(): AdapterDelegatesManager<T> {
        delegates.clear()
        return this
    }

    private fun getDelegateForViewType(viewType: Int): AdapterDelegate<T>? {
        return delegates.get(viewType, fallbackDelegate)
    }

    private fun findUsableDelegate(viewHolder: RecyclerView.ViewHolder): AdapterDelegate<T> {
        val delegate = getDelegateForViewType(viewHolder.itemViewType)
        ObjectHelper.requireNonNull<AdapterDelegate<T>>(delegate, "No delegate found for " + viewHolder + " for item at position = "
                + viewHolder.adapterPosition + " for viewType = " + viewHolder.itemViewType)
        return delegate!!
    }

    companion object {
        private const val VIEW_TYPE_BASELINE = 0X3923
        private const val FALLBACK_DELEGATE_VIEW_TYPE = Integer.MAX_VALUE - 1
    }
}